<?php
/**
 * Cleaner of formatted text
 * 
 * PHP version 5
 *
 * Copyright (c) 2009 Anthony Willis
 * 
 * LICENSE:
 * 
 * This library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any
 * later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * @category  ToolsAndUtilities
 * @package   textCleaner
 * @author    Anthony Willis <antwillis@gmail.com>
 * @copyright 2009 Anthony Willis
 * @license   http://www.gnu.org/licenses/gpl-1.0.html
 * @version   0.1
 * @filesource
 * @link      http://code.google.com/p/phptextcleaner
 * @since     0.1
 * @todo      Finalise documentation
 */

/**
 * Cleaner of formatted text
 *
 * Takes in a string of text and replaces special characters with entity
 * references. This is particularly meant for cleaning Microsoft Word text for
 * placement in HTML files.
 *
 * Additionally, the class will optionally;
 * - separate detected paragraphs in paragraph tags, custom tags, or double
 * line break tags
 * - keep or decode existing HTML tags
 * - turn URLs and/or email addresses into links
 *
 * @category  ToolsAndUtilities
 * @package   textCleaner
 * @author    Anthony Willis <antwillis@gmail.com>
 * @copyright 2009 Anthony Willis
 * @license   http://www.gnu.org/licenses/gpl-1.0.html
 * @version   Release: 0.1
 * @link      http://code.google.com/p/phptextcleaner
 * @since     0.1
 */
class textCleaner {
	/**
	* Custom string to be used to replace line breaks before parsing 
	*/
	const STR_BREAK = "???_!_???";
	/**
	* Error message to display if the custom tags are not provided when the
	* custom tags option is chosen.
	*/	
	const CUSTOM_SEPARATOR_MSG =
			'You need to supply custom start and end tags 
			if you choose custom paragraph seperators.';
	/**
	* Error message to notify that MAX_STR_LENGTH has been exceeded.
	*/
	const LENGTH_MSG =
			'Your text has exceeded the maximum allowable length. 
			Please clean your text in smaller batches.';
    /**
	* This variable is used to hold all pairs of target characters and their
	* replacements. These are additional to the more general replacements made
	* by the PHP htmlentities() function.
	* 
	* @see __construct(), clean()
    * @var array 
	*/
	protected $chars = array();
	
	public function __construct(
		$_max_str_length = 10000, $_custom_replacements  = array()) {
		/**
		  * Build an array of custom character replacements
    	  * 
    	  * Sets up the {@link $chars} arrays.
    	  *
    	  * @return void 
    	  */
		$this->max_str_length = $_max_str_length;
		
		if (is_array($_custom_replacements)) {
			foreach ($_custom_replacements as $custom_replacement) {
				$this->chars[] =
					$this->add_replace_pair(
						$custom_replacement[0],
						$custom_replacement[1]
					);
			}
		}
		
	}
    /**
     * Main public method which takes in a string for cleaning and returns the
     * cleaned string.
     *
     * @param string  $_str				String of text for cleaning
     * @param string  $_break_type		Method of seperating 'paragraphs' of
     * 									text
     * @param string  $_encoding		Encoding type for both HTML output
     * 									and $_str
     * @param boolean $_keep_html_tags	Optionally keep HTML tags encoded
     * @param integer $_write_links		0 = do not create links, 1 = turn URLs
     * 									into HTML links, 2 = turn email
     * 									addresses into HTML links, 3 = turn
     * 									both URLs and email addresses into HTML
     * 									links.
     * @param string  $_start_tag		For a $_break_type of custom tags, this
     * 									is the opening tag
     * @param string  $_end_tag			For a $_break_type of custom tags, this
     * 									is the closing tag
     * @param boolean $_for_textarea	Format result for display in a textarea
     * 
     * @uses correct_original()		Minimial changes to format for re-display
     * @uses prepare_string()		First pass to clean regular errors in string 
     * @uses add_links()			Detect URLs and email addresses and add them
     * @uses replace_characters()	Second pass to replace custom character
     * 								replacements
     * @uses insert_line_breaks()	Add back the appropriate line breaks
     * @uses convert_html_tags()	Replace HTML characters with entity
     * 								references
     * @uses textarea_display()		A slight adjustment for display in a
     * 								textarea
     * @uses MAX_STR_LENGTH			If actual string length is greater than the
     * 								maximum allowed, throw and error and do not
     * 								process.
     * @uses STR_BREAK				Used to replace out line breaks
     * @uses CUSTOM_SEPARATOR_MSG	Added to the error message array on custom
     * 								tag error
     * @uses LENGTH_MSG				Added to the error message array on string
     * 								length error	
     * 
     * @return	array				returns an assosciative array containing
     * 								error messages, original string and cleaned
     * 								string as values.
     */
	public function clean(
		$_str, $_break_type = 'none', $_doc_type = 'html', $_encoding = "utf-8",
		$_keep_html_tags = true, $_write_links = 0, $_start_tag = "",
		$_end_tag = "", $_for_textarea = false) {
		
		$output = array();
		$err_msg = array();
		$new_text = '';
		
		$output['original_text'] = $this->correct_original($_str, $_encoding);
		
		if ($_break_type == 'custom' &&
			(strlen($_start_tag) < 1 || strlen($_end_tag) < 1)) {
			
			$err_msg[] = self::CUSTOM_SEPARATOR_MSG;
				
		}
		
		if (strlen($_str) > $this->max_str_length) {
			
			$err_msg[] = self::LENGTH_MSG;
			$err_msg[] = '(Maximum number of characters allowed is '
				. $this->max_str_length . '. Your text is '
				. strlen($_str) . ' characters long.)';
		
		}
		
		if (count($err_msg) < 1) {
			$_str =
				$this->prepare_string($_str, self::STR_BREAK, $_encoding);
			$_str =
				$this->add_links($_str, $_write_links);
			$_str =
				$this->replace_characters($_str, $this->chars);
			$_str =
				$this->insert_line_breaks(
					$_str,
					self::STR_BREAK,
					$_doc_type,
					$_break_type,
					$_start_tag,
					$_end_tag
				);
			$_str =
				$_keep_html_tags ? $this->convert_html_tags($_str) : $_str;

			$_str =
				$_for_textarea ? $this->textarea_display($_str) : $_str;

			$new_text = $_str;
			
		}
		
		$output['errors'] = $err_msg;
		$output['new_text'] = $new_text;
		
		return $output;
		
	}
	/**
     * Generates an associative array of a character string and its replacement
     * 
     * @param string	$_str_old	Character to detect and replace
     * @param string	$_str_old	Character to replace original with
     * 
     * @see __construct()
     * 
     * @return	array	Associative array of the character and its replacement
     */
	protected function add_replace_pair($_str_old, $_str_new) {
	
		return array('old' => $_str_old, 'new' => $_str_new);
		
	}
    /**
     * Cleans up the original text, correcting artefacts from the form
     * to display correctly on page
     * 
     * @param string	$_str		Original text to manipulate.
     * @param string	$_encoding	Encoding type to be used
     * 
     * @see clean()
     * 
     * @return	string	Cleaned string for display on HTML page
     */
	private function correct_original($_str, $_encoding) {
		return stripslashes(
				htmlentities($_str, ENT_NOQUOTES, $_encoding));
	}
    /**
     * Prepares a string of text for correct display in a textarea for copying
     * and pasting into an HTML document
     * 
     * @param string	$_str	Text to manipulate
     * 
     * @see clean()
     * 
     * @return	string	Cleaned string for display in textarea
     */
	private function textarea_display($_str) {
		
		$_str = stripslashes(str_replace("&", "&amp;", $_str));
		$_str = str_replace(">", "&gt;", str_replace("<", "&lt;", $_str));
		return $_str;
		
	}
    /**
     * Escapes HTML tags
     * 
     * @param string	$_str	Text to manipulate
     * 
     * @see clean()
     * 
     * @return	string	Cleaned string with unescaped characters
     */		
	private function convert_html_tags($_str) {
	
		return str_replace("&gt;", ">", str_replace("&lt;", "<", $_str));

	}
	/**
     * Replaces URLs and/or email addresses with HTML links
     * 
     * @param string	$_str	Text to manipulate
     * @param integer	$_level	0 = do not create links, 1 = turn URLs into HTML
     * 							links, 2 = turn email addresses into HTML links,
     * 							3 = turn both URLs and email addresses into HTML
     * 							links.
     * 
     * @see clean()
     * 
     * @return	string	String with appropriate URLs and/or email addresses
     * 					turned into HTML links
     */
	private function add_links($_str = "", $_selection = "none") {

		$_str = html_entity_decode($_str);

		if ($_selection == "url" || $_selection == "all") {
			$_str =
				eregi_replace(
					'(((ftp|http|https){1}://)[-a-zA-Z0-9@:%_\+.~#?&//=]+)',
					'<a href="\\1">\\1</a>',
					$_str
				);

  			$_str =
 				eregi_replace(
  					'([[:space:]()[{}])(www.[-a-zA-Z0-9@:%_\+.~#?&//=]+)',
  					'\\1<a href="http://\\2">\\2</a>',
  					$_str
  				);
		}
		
		if ($_selection == "email" || $_selection == "all") {
	 		$_str =
	  			eregi_replace(
	  				'([_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3})',
	  				'<a href="mailto:\\1">\\1</a>',
	  				$_str
	  				);
		}
		
		return $_str;

	}
	/**
     * General preparation of a string of text for correct display in an
     * HTML page and replacement of line break with an identifer to
     * enable further processing.
     * 
     * @param string	$_str		Text to manipulate
     * @param string	$_str_break	Replacement string for replacing line breaks
     * @param string	$_encoding	Method of encodng
     * 
     * @see clean()
     * 
     * @return	string	String generally cleaned for display in an HTML page
     * 					with line breaks replaced with $_str_break
     */
	private function prepare_string($_str, $_str_break, $_encoding = "utf-8") {
	
		$_str = htmlentities($_str, ENT_NOQUOTES, $_encoding);
		$_str = str_replace("\n", $_str_break, $_str);
		
		return $_str;
		
	}
	/**
     * Replaces specific characters in a string with alternatives
     * 
     * @param string	$_str			Text to manipulate
     * @param array		$_replacements	Array of associative arrays containing
     * 									specific replacement sets
     * 
     * @see clean()
     * 
     * @return	string	String with specific replacements made
     */
	private function replace_characters($_str, $_replacements) {
	
		foreach ($_replacements as $replacement) {
			if (count($replacement) > 1) {
				$_str =
					str_replace(
						$replacement['old'],
						$replacement['new'],
						$_str
					);
			}
		}
		
		return $_str;
		
	}
	/**
     * Replace a specified string with a chosen type of line break
     * 
     * @param string	$_str			Text to manipulate
     * @param string	$_str_break		String to replace with line breaks
     * @param string	$_break_type	Type of break to insert into string
     * @param string	$_start_tag		For a $_break_type of custom, the
     * 									opening tag used in the replacement
     * @param string	$_end_tag		For a $_break_type of custom, the
     * 									closing tag used in the replacement
     * @see clean()
     * 
     * @return	string	String with all $_str_break instance replaced with
     * 					appropriate line breaks.
     */
	private function insert_line_breaks(
		$_str, $_str_break, $_doc_type = 'html', $_break_type = 'none',
		$_start_tag = "", $_end_tag = "") {
		
		$line_break = ($_doc_type == "xhtml") ? "<br />" : "<br>";

		switch ($_break_type) {
			case 'paragraph':
				$_str =
					"<p>"
					. str_replace(
						$_str_break.$_str_break,
						"</p>\n\r<p>",
						$_str
						)
					. "</p>";
				$_str = str_replace("<p></p>", "", $_str);
				$_str = str_replace($_str_break, $line_break, $_str);
				break;
			case 'break':
				$_str = str_replace($_str_break, $line_break."\n", $_str);
				break;
			case 'custom':
				$_str =
					$_start_tag
					. str_replace(
						$_str_break.$_str_break,
						$_end_tag."\n\r".$_start_tag,
						$_str
						)
					. $_end_tag;
				$_str = str_replace($_start_tag.$_end_tag, "", $_str);
				$_str = str_replace($_str_break, $line_break, $_str);
				break;
		}
		
		$_str = str_replace($_str_break, "\n", $_str);
		
		return $_str;
		
	}

}

?>